Primary Visualization
Fighter Attributes and Outcomes
|>
raw_fighter_df group_by(Stance) |>
summarise(Count = n()) |>
arrange(desc(Count)) |>
mutate(Stance = factor(Stance, levels = unique(Stance))) |>
ggplot(aes(x = Stance, y= Count)) +
geom_bar(stat = "identity", fill = "green2", color = "black") +
theme_minimal(base_size = 15) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
labs(title = "Fighting Stance", x = "Stance", y = "Count")
Key Insights
Fighters’ stances such as orthodox, southpaw, and switch show distinct trends in prevalence. Orthodox is the most common while the side ways stance is the least common.
Weight Class Popularity
|>
data_df group_by(weight_class) |>
summarise(Count = n()) |>
arrange(desc(Count)) |>
mutate(weight_class = factor(weight_class, levels = unique(weight_class))) |>
ggplot(aes(x = weight_class, y = Count)) +
geom_bar(stat = "identity", fill = "green2", color = "black") +
theme_minimal(base_size = 15) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
labs(title = "Weight Classes", x = "Weight Class", y = "Count")
Key Insights
The Lightweight, followed by Welterweight and Middleweight classes, are the most populated. Women’s featherweight, catch weight and open weight classes are less common, indicating possible areas for UFC to expand.
Geographic Trends
## install.packages("maps")
library(maps)
library(tidyverse)
<- ggplot2::map_data("world")
world_df
<- data_df |>
ufc_world separate(location, into = c("City", "State", "Country"), sep = ",")
<- ufc_world |>
country_stat group_by(Country) |>
summarise(n = n()) |>
arrange(desc(n))
$Country <- gsub("\"", "", country_stat$Country)
country_stat
$Country <- trimws(country_stat$Country)
country_stat
<- left_join(world_df, country_stat, by = c("region" = "Country"))
world_full
ggplot(data = world_full, aes(x = long, y = lat, group = group, fill = n)) +
geom_polygon(color = "black") +
coord_fixed(1.3) +
theme_void() +
scale_fill_viridis_c(option = "plasma", na.value = "grey90") +
labs(title = "Number of UFC Fights by Country",
fill = "Number of Fights") +
theme(legend.position = "bottom")
Key Insights
The UFC’s global footprint is expanding, with the United States leading in the number of events hosted. Analyzing city and state data, Las Vegas, Nevada, emerges as the epicenter of UFC fights, a likely result of its iconic status and sports-friendly regulations.
Shiny App
library(shiny)
library(shinythemes)
library(glue)
library(fmsb)
<- data_df |>
choices_R pull(R_fighter) |>
unique()
<- data_df |>
choices_B pull(B_fighter) |>
unique()
<- union(choices_R, choices_B)
choices
<- data_df |>
weights pull(weight_class) |>
unique()
<- fluidPage(
ui titlePanel("UFC Fighter Insights"),
tabsetPanel(
tabPanel("Fighter Statistics Over Time",
theme = shinytheme("darkly"),
sidebarLayout(
sidebarPanel(
selectInput("selectedFighter", "Select a Fighter", choices = choices),
radioButtons("selectedWeight", "Select a Weight Class", choices = weights)
),mainPanel(
tabsetPanel(
tabPanel("Statistics Plot", plotOutput("statsPlot")),
tabPanel("Data Table", dataTableOutput("table"))
)
)
)
),tabPanel("Fighter Comparison",
sidebarLayout(
sidebarPanel(
selectInput("fighter1", "Fighter 1", choices = unique(data_df$R_fighter)),
selectInput("fighter2", "Fighter 2", choices = unique(data_df$B_fighter)),
actionButton("submit", "Submit")
),mainPanel(
plotOutput("plot"),
plotOutput("plot2")
)
)
)
)
)
<- function(input, output, session) {
server # First app logic
observeEvent(input$selectedWeight, {
<- data_df |>
choices filter(weight_class == input$selectedWeight) |>
distinct(R_fighter, B_fighter) |>
pull(R_fighter, B_fighter)
updateSelectInput(session, inputId = "selectedFighter", choices = choices)
})
<- reactive({
ufc_reactive <- data_df |>
fighter_data filter(R_fighter == input$selectedFighter | B_fighter == input$selectedFighter) |>
mutate(Date = as.Date(date)) |>
select(Date, R_fighter, B_fighter,
R_avg_SIG_STR_landed, B_avg_SIG_STR_landed,
R_avg_TD_landed, B_avg_TD_landed,`R_total_time_fought(seconds)`, `B_total_time_fought(seconds)`)
<- fighter_data |>
fighter_stats mutate(SIG_STR_landed = if_else(R_fighter == input$selectedFighter,
R_avg_SIG_STR_landed, B_avg_SIG_STR_landed),TD_landed = if_else(R_fighter == input$selectedFighter,
R_avg_TD_landed, B_avg_TD_landed),Total_time_fought = if_else(
== input$selectedFighter,
R_fighter `R_total_time_fought(seconds)`, `B_total_time_fought(seconds)`)) |>
mutate(across(c(SIG_STR_landed, TD_landed, Total_time_fought), ~ rescale(.x, to = c(0, 1))))
})
$statsPlot <- renderPlot({
outputggplot(ufc_reactive(), aes(x = Date)) +
geom_line(aes(y = SIG_STR_landed, colour = "Significant Strikes Landed")) +
geom_line(aes(y = TD_landed, colour = "Takedowns Landed")) +
geom_line(aes(y = Total_time_fought, colour = "Total Time Fought")) +
labs(title = glue("Performance Over Time"), x = "Date", y = "Stat Value") +
scale_color_manual(values = c("Significant Strikes Landed" = "blue",
"Takedowns Landed" = "red", "Total Time Fought" = "green")) +
theme_minimal()
})
$table <- renderDataTable({
output|>
data_df filter(weight_class == input$selectedWeight)|>
mutate(Date = as.Date(date)) |>
select(Date, R_fighter, B_fighter,
R_avg_SIG_STR_landed, B_avg_SIG_STR_landed,
R_avg_TD_landed, B_avg_TD_landed,`R_total_time_fought(seconds)`, `B_total_time_fought(seconds)`)
})
# Second app logic
<- reactive({
fighter1 |>
data_df filter(R_fighter == input$fighter1) |>
select(R_fighter, R_avg_SIG_STR_landed,
R_avg_TD_landed, R_avg_SUB_ATT, R_avg_REV, |>
R_avg_SIG_STR_pct, R_avg_TD_pct) rename(fighter = R_fighter,
avg_SIG_STR_landed = R_avg_SIG_STR_landed,
avg_TD_landed = R_avg_TD_landed,
avg_SUB_ATT = R_avg_SUB_ATT,
avg_REV = R_avg_REV,
avg_SIG_STR_pct = R_avg_SIG_STR_pct,
avg_TD_pct = R_avg_TD_pct)
})
<- reactive({
fighter2 |>
data_df filter(B_fighter == input$fighter2) |>
select(B_fighter, B_avg_SIG_STR_landed,
|>
B_avg_TD_landed, B_avg_SUB_ATT, B_avg_REV, B_avg_SIG_STR_pct, B_avg_TD_pct) rename(fighter = B_fighter,
avg_SIG_STR_landed = B_avg_SIG_STR_landed,
avg_TD_landed = B_avg_TD_landed,
avg_SUB_ATT = B_avg_SUB_ATT,
avg_REV = B_avg_REV,
avg_SIG_STR_pct = B_avg_SIG_STR_pct,
avg_TD_pct = B_avg_TD_pct)
})
<- reactive({
fighter_full_df
<- bind_rows(fighter1(), fighter2())
fighter_full
<- fighter_full |>
fighter_full drop_na() |>
group_by(fighter) |>
summarise(avg_SIG_STR_landed = mean(avg_SIG_STR_landed),
avg_TD_landed = mean(avg_TD_landed),
avg_SUB_ATT = mean(avg_SUB_ATT),
avg_REV = mean(avg_REV),
avg_SIG_STR_pct = mean(avg_SIG_STR_pct),
avg_TD_pct = mean(avg_TD_pct))
<- fighter_full |>
max_values select(-fighter) |>
summarise_all(max)
<- fighter_full |>
min_values select(-fighter) |>
summarise_all(min)
<- bind_rows(max_values, min_values, fighter_full)
fighter_full
<- fighter_full |>
fighter_comp filter(fighter %in% c(input$fighter1, input$fighter2))
<- apply(fighter_comp[, 1:6], 2, max)
max_values
<- apply(fighter_comp[, 1:6], 2, function(x) min(x) - (max(x) * 0.1))
min_values
<- fighter_comp |>
fighter_comp_2 select(-fighter)
<- as.data.frame(fighter_comp_2)
fighter_comp_df
rownames(fighter_comp_df) <- c(input$fighter1, input$fighter2)
<- rbind(max_values, min_values, fighter_comp_df)
radar_data
})
$plot2 <- renderPlot({
outputif(input$submit == 0) {
return("Please select two fighters and click submit")
}
# Set graphic colors
library(RColorBrewer)
<- brewer.pal(3, "Accent")
coul <- coul
colors_border library(scales)
<- alpha(coul,0.3)
colors_in
radarchart(fighter_full_df(),
axistype = 5,
#custom polygon
pcol = colors_border, pfcol =
plwd = 4, plty = 1,
colors_in, #custom the grid
cglcol = "grey", cglty = 1, axislabcol = "black",
caxislabels = seq(0, 20, 5), cglwd = 0.8,
#custom labels
vlcex = 0.8
)# Add a legend
legend("topright",
legend = rownames(fighter_full_df()[-c(1, 2),]),
fill = colors_in,
border = colors_border,
bty = "n",
cex = 0.8,
title = "Fighter")
})
$plot <- renderPlot({
output
if(input$submit == 0) {
return("Please select two fighters and click submit")
}
<- bind_rows(fighter1(), fighter2())
fighter_full
<- fighter_full |>
fighter_full drop_na() |>
group_by(fighter) |>
summarise(avg_SIG_STR_landed = mean(avg_SIG_STR_landed),
avg_TD_landed = mean(avg_TD_landed),
avg_SUB_ATT = mean(avg_SUB_ATT),
avg_REV = mean(avg_REV),
avg_SIG_STR_pct = mean(avg_SIG_STR_pct),
avg_TD_pct = mean(avg_TD_pct))
|>
fighter_full filter(fighter %in% c(input$fighter1, input$fighter2)) |>
pivot_longer(cols = -fighter, names_to = "stat", values_to = "value") |>
ggplot(aes(x = fighter, y = value, fill = fighter)) +
geom_segment(aes(xend = fighter, yend = 0),
color =
"black") +
geom_point(stat =
"identity",
position = position_dodge(width = 0.5), size = 3, aes(color = fighter)) +
facet_wrap(~stat,
scales = "free_y") +
labs(title = "Fighter Stats",
x = "Stat",
y = "Value") +
theme_minimal() +
theme(axis.text.x =
element_text(
angle = 0, hjust = 0.5),
strip.background =
element_blank(),
strip.text.x =
element_text(size = 10))
})
}
shinyApp(ui, server)
Static Look at the App
Key Insights
- The app allows users to select a fighter from the dropdown menu to view how their statistics change over time.
- The app allows users to compare two fighters based on their average stats in various categories through a lolipop chart.
- The app uses a radar chart to visualize the comparison between the two fighters.
- The radar chart provides a visual representation of the fighters’ stats in different categories, such as average significant strikes landed, average takedowns landed, average submission attempts, average reversals, average significant strike percentage, and average takedown percentage.
- The radar chart highlights the strengths and weaknesses of each fighter, allowing users to compare their performance in different areas.
Conclusion
Conclusively, the world of UFC is vast and filled with talented fighters. The app provides a platform for users to explore the statistics of their favorite fighters and compare them with others. The app’s interactive features allow users to select fighters from the dropdown menu and view their stats over time. The lolipop chart and radar chart provide a visual representation of the fighters’ average stats in different categories, making it easier for users to compare their performance. The app is a valuable resource for UFC fans and enthusiasts who want to learn more about their favorite fighters and their performance in the ring. The sport is most prominent in the United States, but it has a global fan base that continues to grow. The app is a testament to the popularity of UFC and the interest in the sport worldwide.